/**
 * Radio Story Cards - using RFID tags to track physical story cards in an agile/lean environment.
 *
 * See http://www.radiostorycards.com for more information.
 *
 *
 * Copyright (C) 2011, Ted M. Young
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */
package com.radiostorycards.comm;

import com.google.common.base.Ascii;
import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.base.Splitter;
import com.google.common.collect.Iterables;
import com.radiostorycards.domain.tags.RfidTag;

import java.io.IOException;

/**
 * Translates the incoming stream of characters (as bytes) from the Parallax RFID Reader (USB) to the 5-byte long RFID
 * Tag ID.
 *
 * @author Ted Young <tedyoung@gmail.com> @JitterTed
 */
public class ParallaxTranslator extends AbstractRfidReaderTagTranslator {

  public static final char START_BYTE = Ascii.LF;
  public static final char STOP_BYTE = Ascii.CR;
  public static final int ASCII_CHARS_IN_TAG_ID = BYTES_IN_TAG_ID * 2;

  @Override
  public boolean isEndOfTag(char inputByte) {
    return inputByte == STOP_BYTE;
  }

  /**
   * Expects the input to be of the form:
   * <p/>
   * <code>0x0A</code> (start byte), then 10 hex-digits in ASCII format, e.g:
   * <p/>
   * <code>A1B2C3D4EF</code>
   * <p/>
   * Which translates to the bytes:
   * <p/>
   * <code>0xA1, 0xB2, 0xC3, 0xD4, 0xEF</code>
   * <p/>
   * The start byte is optional. The stop byte is not expected as it would not have been added to the input stream.
   *
   * @param rawRfidInputString the raw input string from the Reader
   * @return a new RfidTag with the Rfid bytes
   * @throws IOException
   */
  @Override
  public RfidTag rfidStringToTag(String rawRfidInputString) throws IOException {
    Iterable<Byte> tagAsBytes = rfidStringToTagBytes(rawRfidInputString);
    return new RfidTag(tagAsBytes);
  }

  Iterable<Byte> rfidStringToTagBytes(String rawRfidInputString) {
    String tagIdString = stripStartByteFrom(rawRfidInputString);
    return asciiHexToBytes(tagIdString);
  }

  private Iterable<Byte> asciiHexToBytes(String tagIdString) {
    Preconditions.checkArgument(tagIdString.length() == ASCII_CHARS_IN_TAG_ID,
        "Wrong number of characters for RFID Tag string. Expected %s hex characters, but received the string: %s",
        ASCII_CHARS_IN_TAG_ID, tagIdString);

    Iterable<String> hexValues = Splitter.fixedLength(2).split(tagIdString);

    return Iterables.transform(hexValues, new StringToByteTransformer());
  }

  private String stripStartByteFrom(String rawRfidInputString) {
    if (firstCharIsStartByte(rawRfidInputString)) {
      return rawRfidInputString.substring(1);
    } else {
      return rawRfidInputString;
    }
  }

  private boolean firstCharIsStartByte(String rawRfidInputString) {
    return rawRfidInputString.charAt(0) == START_BYTE;
  }

  private class StringToByteTransformer implements Function<String, Byte> {
    public Byte apply(String input) {
      return stringToUnsignedByte(input);
    }
  }
}
